home *** CD-ROM | disk | FTP | other *** search
/ Aminet 1 (Walnut Creek) / Aminet - June 1993 [Walnut Creek].iso / aminet / util / gnu / textutl3.lha / textutils-1.3 / src / nl.c < prev    next >
C/C++ Source or Header  |  1992-06-29  |  12KB  |  547 lines

  1. /* nl -- number lines of files
  2.    Copyright (C) 1989, 1992 Free Software Foundation, Inc.
  3.  
  4.    This program is free software; you can redistribute it and/or modify
  5.    it under the terms of the GNU General Public License as published by
  6.    the Free Software Foundation; either version 2, or (at your option)
  7.    any later version.
  8.  
  9.    This program is distributed in the hope that it will be useful,
  10.    but WITHOUT ANY WARRANTY; without even the implied warranty of
  11.    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  12.    GNU General Public License for more details.
  13.  
  14.    You should have received a copy of the GNU General Public License
  15.    along with this program; if not, write to the Free Software
  16.    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.  */
  17.  
  18. /* Written by Scott Bartram (nancy!scott@uunet.uu.net)
  19.    Revised by David MacKenzie (djm@ai.mit.edu) */
  20.  
  21. #include <stdio.h>
  22. #include <sys/types.h>
  23. #include <getopt.h>
  24. #include <regex.h>
  25. #include "linebuffer.h"
  26. #include "system.h"
  27.  
  28. #ifndef TRUE
  29. #define TRUE   1
  30. #define FALSE  0
  31. #endif
  32.  
  33. /* Line-number formats. */
  34. enum number_format
  35. {
  36.   FORMAT_RIGHT_NOLZ,        /* Right justified, no leading zeroes.  */
  37.   FORMAT_RIGHT_LZ,        /* Right justified, leading zeroes.  */
  38.   FORMAT_LEFT            /* Left justified, no leading zeroes.  */
  39. };
  40.  
  41. /* Default section delimiter characters.  */
  42. #define DEFAULT_SECTION_DELIMITERS  "\\:"
  43.  
  44. /* Types of input lines: either one of the section delimiters,
  45.    or text to output. */
  46. enum section
  47. {
  48.   Header, Body, Footer, Text
  49. };
  50.  
  51. /* Format of body lines (-b).  */
  52. char *body_type = "t";
  53.  
  54. /* Format of header lines (-h).  */
  55. char *header_type = "n";
  56.  
  57. /* Format of footer lines (-f).  */
  58. char *footer_type = "n";
  59.  
  60. /* Format currently being used (body, header, or footer).  */
  61. char *current_type;
  62.  
  63. /* Regex for body lines to number (-bp).  */
  64. struct re_pattern_buffer body_regex;
  65.  
  66. /* Regex for header lines to number (-hp).  */
  67. struct re_pattern_buffer header_regex;
  68.  
  69. /* Regex for footer lines to number (-fp).  */
  70. struct re_pattern_buffer footer_regex;
  71.  
  72. /* Pointer to current regex, if any.  */
  73. struct re_pattern_buffer *current_regex = NULL;
  74.  
  75. /* Separator string to print after line number (-s).  */
  76. char *separator_str = "\t";
  77.  
  78. /* Input section delimiter string (-d).  */
  79. char *section_del = DEFAULT_SECTION_DELIMITERS;
  80.  
  81. /* Header delimiter string.  */
  82. char *header_del = NULL;
  83.  
  84. /* Header section delimiter length.  */
  85. int header_del_len;
  86.  
  87. /* Body delimiter string.  */
  88. char *body_del = NULL;
  89.  
  90. /* Body section delimiter length.  */
  91. int body_del_len;
  92.  
  93. /* Footer delimiter string.  */
  94. char *footer_del = NULL;
  95.  
  96. /* Footer section delimiter length.  */
  97. int footer_del_len;
  98.  
  99. /* Input buffer.  */
  100. struct linebuffer line_buf;
  101.  
  102. /* printf format string for line number.  */
  103. char *print_fmt;
  104.  
  105. /* printf format string for unnumbered lines.  */
  106. char *print_no_line_fmt = NULL;
  107.  
  108. /* Starting line number on each page (-v).  */
  109. int page_start = 1;
  110.  
  111. /* Line number increment (-i).  */
  112. int page_incr = 1;
  113.  
  114. /* If TRUE, reset line number at start of each page (-p).  */
  115. int reset_numbers = TRUE;
  116.  
  117. /* Number of blank lines to consider to be one line for numbering (-l).  */
  118. int blank_join = 1;
  119.  
  120. /* Width of line numbers (-w).  */
  121. int lineno_width = 6;
  122.  
  123. /* Line number format (-n).  */
  124. enum number_format lineno_format = FORMAT_RIGHT_NOLZ;
  125.  
  126. /* Current print line number.  */
  127. int line_no;
  128.  
  129. /* The name this program was run with. */
  130. char *program_name;
  131.  
  132. /* Nonzero if we have ever read standard input. */
  133. int have_read_stdin;
  134.  
  135. enum section check_section ();
  136. char *xmalloc ();
  137. char *xrealloc ();
  138. int build_type_arg ();
  139. int nl_file ();
  140. void usage ();
  141. void process_file ();
  142. void proc_header ();
  143. void proc_body ();
  144. void proc_footer ();
  145. void proc_text ();
  146. void print_lineno ();
  147. void build_print_fmt ();
  148. void error ();
  149.  
  150. struct option longopts[] =
  151. {
  152.   {"header-numbering", 1, NULL, 'h'},
  153.   {"body-numbering", 1, NULL, 'b'},
  154.   {"footer-numbering", 1, NULL, 'f'},
  155.   {"first-page", 1, NULL, 'v'},
  156.   {"page-increment", 1, NULL, 'i'},
  157.   {"no-renumber", 0, NULL, 'p'},
  158.   {"join-blank-lines", 1, NULL, 'l'},
  159.   {"number-separator", 1, NULL, 's'},
  160.   {"number-width", 1, NULL, 'w'},
  161.   {"number-format", 1, NULL, 'n'},
  162.   {"section-delimiter", 1, NULL, 'd'},
  163.   {NULL, 0, NULL, 0}
  164. };
  165.  
  166. void
  167. main (argc, argv)
  168.      int argc;
  169.      char **argv;
  170. {
  171.   int c, exit_status = 0;
  172.  
  173.   program_name = argv[0];
  174.   have_read_stdin = 0;
  175.  
  176.   while ((c = getopt_long (argc, argv, "h:b:f:v:i:pl:s:w:n:d:", longopts,
  177.                (int *) 0)) != EOF)
  178.     {
  179.       switch (c)
  180.     {
  181.     case 'h':
  182.       if (build_type_arg (&header_type, &header_regex) != TRUE)
  183.         usage ();
  184.       break;
  185.     case 'b':
  186.       if (build_type_arg (&body_type, &body_regex) != TRUE)
  187.         usage ();
  188.       break;
  189.     case 'f':
  190.       if (build_type_arg (&footer_type, &footer_regex) != TRUE)
  191.         usage ();
  192.       break;
  193.     case 'v':
  194.       page_start = atoi (optarg);
  195.       break;
  196.     case 'i':
  197.       page_incr = atoi (optarg);
  198.       if (page_incr < 1)
  199.         page_incr = 1;
  200.       break;
  201.     case 'p':
  202.       reset_numbers = FALSE;
  203.       break;
  204.     case 'l':
  205.       blank_join = atoi (optarg);
  206.       break;
  207.     case 's':
  208.       separator_str = optarg;
  209.       break;
  210.     case 'w':
  211.       lineno_width = atoi (optarg);
  212.       if (lineno_width < 1)
  213.         lineno_width = 1;
  214.       break;
  215.     case 'n':
  216.       switch (*optarg)
  217.         {
  218.         case 'l':
  219.           if (optarg[1] == 'n')
  220.         lineno_format = FORMAT_LEFT;
  221.           else
  222.         usage ();
  223.           break;
  224.         case 'r':
  225.           switch (optarg[1])
  226.         {
  227.         case 'n':
  228.           lineno_format = FORMAT_RIGHT_NOLZ;
  229.           break;
  230.         case 'z':
  231.           lineno_format = FORMAT_RIGHT_LZ;
  232.           break;
  233.         default:
  234.           usage ();
  235.           break;
  236.         }
  237.           break;
  238.         default:
  239.           usage ();
  240.           break;
  241.         }
  242.       break;
  243.     case 'd':
  244.       section_del = optarg;
  245.       break;
  246.     default:
  247.       usage ();
  248.       break;
  249.     }
  250.     }
  251.  
  252.   /* Initialize the section delimiters.  */
  253.   c = strlen (section_del);
  254.  
  255.   header_del_len = c * 3;
  256.   header_del = xmalloc (header_del_len + 1);
  257.   strcat (strcat (strcpy (header_del, section_del), section_del), section_del);
  258.  
  259.   body_del_len = c * 2;
  260.   body_del = xmalloc (body_del_len + 1);
  261.   strcat (strcpy (body_del, section_del), section_del);
  262.  
  263.   footer_del_len = c;
  264.   footer_del = xmalloc (footer_del_len + 1);
  265.   strcpy (footer_del, section_del);
  266.  
  267.   /* Initialize the input buffer.  */
  268.   initbuffer (&line_buf);
  269.  
  270.   /* Initialize the printf format for unnumbered lines. */
  271.   c = strlen (separator_str);
  272.   print_no_line_fmt = xmalloc (lineno_width + c + 1);
  273.   memset (print_no_line_fmt, ' ', lineno_width + c);
  274.   print_no_line_fmt[lineno_width + c] = '\0';
  275.  
  276.   line_no = page_start;
  277.   current_type = body_type;
  278.   current_regex = &body_regex;
  279.   build_print_fmt ();
  280.  
  281.   /* Main processing. */
  282.  
  283.   if (optind == argc)
  284.     exit_status |= nl_file ("-");
  285.   else
  286.     for (; optind < argc; optind++)
  287.       exit_status |= nl_file (argv[optind]);
  288.  
  289.   if (have_read_stdin && fclose (stdin) == EOF)
  290.     {
  291.       error (0, errno, "-");
  292.       exit_status = 1;
  293.     }
  294.   if (ferror (stdout) || fclose (stdout) == EOF)
  295.     error (1, 0, "write error");
  296.  
  297.   exit (exit_status);
  298. }
  299.  
  300. /* Process file FILE to standard output.
  301.    Return 0 if successful, 1 if not. */
  302.  
  303. int
  304. nl_file (file)
  305.      char *file;
  306. {
  307.   FILE *stream;
  308.  
  309.   if (!strcmp (file, "-"))
  310.     {
  311.       have_read_stdin = 1;
  312.       stream = stdin;
  313.     }
  314.   else
  315.     {
  316.       stream = fopen (file, "r");
  317.       if (stream == NULL)
  318.     {
  319.       error (0, errno, "%s", file);
  320.       return 1;
  321.     }
  322.     }
  323.  
  324.   process_file (stream);
  325.  
  326.   if (ferror (stream))
  327.     {
  328.       error (0, errno, "%s", file);
  329.       return 1;
  330.     }
  331.   if (!strcmp (file, "-"))
  332.     clearerr (stream);        /* Also clear EOF. */
  333.   else if (fclose (stream) == EOF)
  334.     {
  335.       error (0, errno, "%s", file);
  336.       return 1;
  337.     }
  338.   return 0;
  339. }
  340.  
  341. /* Read and process the file pointed to by FP. */
  342.  
  343. void
  344. process_file (fp)
  345.      FILE *fp;
  346. {
  347.   while (readline (&line_buf, fp))
  348.     {
  349.       switch ((int) check_section ())
  350.     {
  351.     case Header:
  352.       proc_header ();
  353.       break;
  354.     case Body:
  355.       proc_body ();
  356.       break;
  357.     case Footer:
  358.       proc_footer ();
  359.       break;
  360.     case Text:
  361.       proc_text ();
  362.       break;
  363.     }
  364.     }
  365. }
  366.  
  367. /* Return the type of line in `line_buf'. */
  368.  
  369. enum section
  370. check_section ()
  371. {
  372.   if (line_buf.length < 2 || memcmp (line_buf.buffer, section_del, 2))
  373.     return Text;
  374.   if (line_buf.length == header_del_len
  375.       && !memcmp (line_buf.buffer, header_del, header_del_len))
  376.     return Header;
  377.   if (line_buf.length == body_del_len
  378.       && !memcmp (line_buf.buffer, body_del, body_del_len))
  379.     return Body;
  380.   if (line_buf.length == footer_del_len
  381.       && !memcmp (line_buf.buffer, footer_del, footer_del_len))
  382.     return Footer;
  383.   return Text;
  384. }
  385.  
  386. /* Switch to a header section. */
  387.  
  388. void
  389. proc_header ()
  390. {
  391.   current_type = header_type;
  392.   current_regex = &header_regex;
  393.   if (reset_numbers)
  394.     line_no = page_start;
  395.   putchar ('\n');
  396. }
  397.  
  398. /* Switch to a body section. */
  399.  
  400. void
  401. proc_body ()
  402. {
  403.   current_type = body_type;
  404.   current_regex = &body_regex;
  405.   putchar ('\n');
  406. }
  407.  
  408. /* Switch to a footer section. */
  409.  
  410. void
  411. proc_footer ()
  412. {
  413.   current_type = footer_type;
  414.   current_regex = &footer_regex;
  415.   putchar ('\n');
  416. }
  417.  
  418. /* Process a regular text line in `line_buf'. */
  419.  
  420. void
  421. proc_text ()
  422. {
  423.   static int blank_lines = 0;    /* Consecutive blank lines so far. */
  424.  
  425.   switch (*current_type)
  426.     {
  427.     case 'a':
  428.       if (blank_join > 1)
  429.     {
  430.       if (line_buf.length || ++blank_lines == blank_join)
  431.         {
  432.           print_lineno ();
  433.           blank_lines = 0;
  434.         }
  435.       else
  436.         printf (print_no_line_fmt);
  437.     }
  438.       else
  439.     print_lineno ();
  440.       break;
  441.     case 't':
  442.       if (line_buf.length)
  443.     print_lineno ();
  444.       else
  445.     printf (print_no_line_fmt);
  446.       break;
  447.     case 'n':
  448.       printf (print_no_line_fmt);
  449.       break;
  450.     case 'p':
  451.       if (re_search (current_regex, line_buf.buffer, line_buf.length,
  452.              0, line_buf.length, (struct re_registers *) 0) < 0)
  453.     printf (print_no_line_fmt);
  454.       else
  455.     print_lineno ();
  456.       break;
  457.     }
  458.   fwrite (line_buf.buffer, sizeof (char), line_buf.length, stdout);
  459.   putchar ('\n');
  460. }
  461.  
  462. /* Print and increment the line number. */
  463.  
  464. void
  465. print_lineno ()
  466. {
  467.   printf (print_fmt, line_no);
  468.   line_no += page_incr;
  469. }
  470.  
  471. /* Build the printf format string, based on `lineno_format'. */
  472.  
  473. void
  474. build_print_fmt ()
  475. {
  476.   /* 12 = 10 chars for lineno_width, 1 for %, 1 for \0.  */
  477.   print_fmt = xmalloc (strlen (separator_str) + 12);
  478.   switch (lineno_format)
  479.     {
  480.     case FORMAT_RIGHT_NOLZ:
  481.       sprintf (print_fmt, "%%%dd%s", lineno_width, separator_str);
  482.       break;
  483.     case FORMAT_RIGHT_LZ:
  484.       sprintf (print_fmt, "%%0%dd%s", lineno_width, separator_str);
  485.       break;
  486.     case FORMAT_LEFT:
  487.       sprintf (print_fmt, "%%-%dd%s", lineno_width, separator_str);
  488.       break;
  489.     }
  490. }
  491.  
  492. /* Set the command line flag TYPEP and possibly the regex pointer REGEXP,
  493.    according to `optarg'.  */
  494.  
  495. int
  496. build_type_arg (typep, regexp)
  497.      char **typep;
  498.      struct re_pattern_buffer *regexp;
  499. {
  500.   char *errmsg;
  501.   int rval = TRUE;
  502.   int optlen;
  503.  
  504.   switch (*optarg)
  505.     {
  506.     case 'a':
  507.     case 't':
  508.     case 'n':
  509.       *typep = optarg;
  510.       break;
  511.     case 'p':
  512.       *typep = optarg++;
  513.       optlen = strlen (optarg);
  514.       regexp->allocated = optlen * 2;
  515.       regexp->buffer = (unsigned char *) xmalloc (regexp->allocated);
  516.       regexp->translate = NULL;
  517.       regexp->fastmap = xmalloc (256);
  518.       regexp->fastmap_accurate = 0;
  519.       errmsg = re_compile_pattern (optarg, optlen, regexp);
  520.       if (errmsg)
  521.     error (1, 0, "%s", errmsg);
  522.       break;
  523.     default:
  524.       rval = FALSE;
  525.       break;
  526.     }
  527.   return rval;
  528. }
  529.  
  530. /* Print a usage message and quit. */
  531.  
  532. void
  533. usage ()
  534. {
  535.   fprintf (stderr, "\
  536. Usage: %s [-h header-style] [-b body-style] [-f footer-style] [-p] [-d cc]\n\
  537.        [-v start-number] [-i increment] [-l lines] [-s line-separator]\n\
  538.        [-w line-no-width] [-n {ln,rn,rz}] [--header-numbering=style]\n\
  539.        [--body-numbering=style] [--footer-numbering=style]\n\
  540.        [--first-page=number] [--page-increment=number] [--no-renumber]\n\
  541.        [--join-blank-lines=number] [--number-separator=string]\n\
  542.        [--number-width=number] [--number-format={ln,rn,rz}]\n\
  543.        [--section-delimiter=cc] [file...]\n",
  544.        program_name);
  545.   exit (2);
  546. }
  547.